#include <linux/init.h>
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define DEVNAME "sleepy"
#define DEVCOUNT 1
#define BASEMINOR 0

static dev_t sleepy_dev;
static int sleepy_major = 0;
static struct cdev *sleepy_cdev;
static DECLARE_WAIT_QUEUE_HEAD(sleepy_wq);
static int flag = 0;

module_param(sleepy_major, int, 0);

/*page 151*/
ssize_t sleepy_read (struct file *filp, char __user * buf, 
				size_t count, loff_t * pos)
{
	printk(KERN_DEBUG "process %i (%s) going to sleep\n",
			current->pid, current->comm);

	wait_event_interruptible(sleepy_wq, flag != 0);
	flag = 0;

	printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
	return 0;
}

ssize_t sleepy_write (struct file *filp, const char __user * buf, 
				size_t count, loff_t * pos)
{
	printk(KERN_DEBUG "process %i (%s) awakening the readers ...\n", 
			current->pid, current->comm);

	flag = 1;
	wake_up_interruptible(&sleepy_wq);

	return count;
}


struct file_operations sleepy_fops = {
	.owner = THIS_MODULE,
	.read = sleepy_read,
	.write = sleepy_write,
};

static void sleepy_exit(void)
{
	printk(KERN_DEBUG "sleepy exit ...\n");
	cdev_del(sleepy_cdev);
	unregister_chrdev_region(sleepy_dev, DEVCOUNT);
}

static int sleepy_init(void)
{
	int result;
	if (!sleepy_major) { /*dynamic alloc*/
		result = alloc_chrdev_region(&sleepy_dev, BASEMINOR, 
					DEVCOUNT, DEVNAME);
		if (result < 0)
			return result;
		sleepy_major = MAJOR(sleepy_dev);
	} else {
		sleepy_dev = MKDEV(sleepy_major, BASEMINOR);
		result = register_chrdev_region(sleepy_dev, DEVCOUNT, DEVNAME);
		if (result < 0)
			return result;
	}

	sleepy_cdev = cdev_alloc();
	sleepy_cdev->ops = &sleepy_fops;
	cdev_init(sleepy_cdev, &sleepy_fops);
	sleepy_cdev->owner = THIS_MODULE;
	result = cdev_add(sleepy_cdev, sleepy_dev, DEVCOUNT);
	if (result < 0) {
		sleepy_exit();
		return result;
	}
	printk(KERN_DEBUG "sleepy init, result = %i\n", result);
	return 0;
}

module_init(sleepy_init);
module_exit(sleepy_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("LiuGuiChao");

